﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Alex.Library.Specifications
{
    /// <summary>
    /// rebind lambda expression parameter
    /// </summary>
    internal class ParameterRebinder : ExpressionVisitor
    {
        #region Private Fields

        private readonly Dictionary<ParameterExpression, ParameterExpression> map;
        
        #endregion

        #region Ctor
        
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="map"></param>
        internal ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
        {
            this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
        }
        
        #endregion

        #region Internal Static Methods
        
        /// <summary>
        /// rebind parameters
        /// </summary>
        /// <param name="map"></param>
        /// <param name="exp"></param>
        /// <returns></returns>
        internal static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
        {
            return new ParameterRebinder(map).Visit(exp);
        }
        
        #endregion

        #region Protected Methods
        
        /// <summary>
        /// visit parameter
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        protected override Expression VisitParameter(ParameterExpression p)
        {
            ParameterExpression replacement;
            if (map.TryGetValue(p, out replacement))
            {
                p = replacement;
            }
            return base.VisitParameter(p);
        }

        #endregion
    }
}
